package com.base.service.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.base.service.QueryLogService;
import com.base.service.SqlStatementBuilder;
import com.base.dialects.DialectFactory;
import com.base.dialects.Dialects;
import com.base.mapper.DataSourceMapper;
import com.base.query.CustomizeQueryWrapper;
import com.base.service.DataSourceService;
import com.base.service.RepositorySourceService;
import com.base.utils.QueryLogTheadLocalUtils;
import com.base.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 *
 */
@Service
@Slf4j
public class DataSourceServiceImpl extends ServiceImpl<DataSourceMapper, DataSource>
        implements DataSourceService {

    @Autowired
    javax.sql.DataSource dataSource;

    @Autowired
    DataSourceCreator dataSourceCreator;

    @Autowired
    RepositorySourceService repositorySourceService;

    @Autowired
    QueryLogService queryLogService;

    private final HashMap<Serializable, RepositorySource> cacheRepositorySource = new HashMap<>();


    /**
     * 分页查询
     */
    @Override
    public IPage<DataSource> page(PageRequestHelperVo pageRequestHelperVo) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.like(StringUtils.isNotBlank(pageRequestHelperVo.getKeyword()), "name", pageRequestHelperVo.getKeyword());
        wrapper.eq(StringUtils.isNotBlank(pageRequestHelperVo.getType()), "repository_id", pageRequestHelperVo.getType());
        IPage<DataSource> page = new Page<>(pageRequestHelperVo.getPageNum(), pageRequestHelperVo.getPageSize());
        return page(page, wrapper);
    }

    /**
     * 执行查询，返回查询结果
     *
     * @param execQueryVo 执行查询对象
     * @return 封装查询结果的分页对象
     */
    @Override
    public Page execQuery(ExecQueryVo execQueryVo) {
        Assert.notNull(execQueryVo.getRepositorySourceId(), "传入的执行查询对象有误，没有指定数据连接，请检查！" + execQueryVo);
        String dataSourceKey = String.valueOf(execQueryVo.getRepositorySourceId());

        //切换数据源
        changeDataSource(dataSourceKey);
        DbType dbType = JdbcUtils.getDbType(cacheRepositorySource.get(dataSourceKey).getUrl());
        //设置分页
        Map<String, Object> limit = execQueryVo.getLimit();
        Page page = new Page();
        page.setSearchCount(false);
        if (limit != null) {
            page.setSize(limit.get("take") == null ? 0 : (int) limit.get("take"));
        }

        //构建查询条件
        QueryWrapper queryWrapper = builderSql(execQueryVo, page, dbType);

        QueryLog queryLog = createQueryLog(execQueryVo);
        Page returnVal;
        try {
            //同环比查询的方法
            if (execQueryVo.getIsYoy()) {
                //                page.setSize(-1);
                returnVal = this.getBaseMapper().selectYoYBySql(page, execQueryVo.getYoyType(), execQueryVo.getSql(), queryWrapper);
            } else {
                returnVal = this.getBaseMapper().selectBySql(page, execQueryVo.getSql(), queryWrapper);
            }
            return returnVal;
        } catch (Exception e) {
            log.error(e.getMessage());
            queryLog.setExecResult((byte) 0);
            queryLog.setExecMsg(e.getMessage());
            throw e;
        } finally {
            DynamicDataSourceContextHolder.clear();
            queryLogService.asyncSaveOrUpdate(queryLog);
        }

    }

    /**
     * 构建SQL
     *
     * @param execQueryVo
     * @param page
     * @return
     */
    private QueryWrapper builderSql(ExecQueryVo execQueryVo, Page page, DbType dbType) {
        SqlStatementBuilder sqlStatementBuilder = new DefaultSqlStatementBuilderImpl();
        CustomizeQueryWrapper queryWrapper = new CustomizeQueryWrapper();
        Dialects dialect = DialectFactory.getDialect(dbType);
        sqlStatementBuilder.builderSelect(execQueryVo.getFields(), queryWrapper, dialect);
        sqlStatementBuilder.builderWhere(execQueryVo.getFilters(), queryWrapper, dialect);
        sqlStatementBuilder.builderOrderBy(execQueryVo.getFields(), queryWrapper);
        sqlStatementBuilder.builderGroup(execQueryVo.getFields(), queryWrapper);
        return queryWrapper;
    }

    /**
     * 切换数据源
     *
     * @param key
     */
    private void changeDataSource(String key) {
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
        if (!dynamicRoutingDataSource.getCurrentDataSources().containsKey(key)) {
            RepositorySource repositorySource = repositorySourceService.getById(key);
            cacheRepositorySource.put(key, repositorySource);
            javax.sql.DataSource dataSource = createDataSource(repositorySource);
            dynamicRoutingDataSource.addDataSource(key, dataSource);
        }
        DynamicDataSourceContextHolder.push(key);
    }

    /**
     * 创建数据源
     *
     * @param repositorySource
     * @return
     */
    private javax.sql.DataSource createDataSource(RepositorySource repositorySource) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(repositorySource, dataSourceProperty);
        return dataSourceCreator.createDataSource(dataSourceProperty);
    }

    private QueryLog createQueryLog(ExecQueryVo execQueryVo) {
        QueryLog queryLog = new QueryLog();
        queryLog.setDashboardId(execQueryVo.getDashboardId());
        queryLog.setDataSourceId(execQueryVo.getDataSourceId());
        queryLog.setWorkBookId(execQueryVo.getWorkBookId());
        queryLog.setRepositorySourceId(execQueryVo.getRepositorySourceId());
        QueryLogTheadLocalUtils.set(queryLog);
        return queryLog;
    }
}
